﻿
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using Red.Controls.Utils;

namespace Red.Controls.Shell
{
    public partial class DropDownContent : UserControl
    {
        #region Properties
        
        public DropDown Owner { get; set; }
        
        #endregion Properties

        #region C-tor
        
        public DropDownContent()
        {
            InitializeComponent();
        }
        
        #endregion C-tor

        #region Event handlers
        
        private async void Button_Click( object sender, RoutedEventArgs e )
        {
            await Owner.DropAsync();
        }
        
        #endregion Event handlers
    }

    internal class DropDownWindow : RedDialog
    {
        #region Data
        
        private bool _closing = false;
        private readonly Grid _contentPanel;
        
        #endregion Data

        #region C-tor
        
        public DropDownWindow( DropDown dropDown, UIElement content, object dataContext, RedDialogType type )
            : base( dropDown )
        {
            ShowTitleBar = false;
            IsWindowDraggable = false;
            Type = type;
            WindowStartupLocation = WindowStartupLocation.Manual;
            SizeToContent = SizeToContent.WidthAndHeight;
            PreviewKeyDown += DropDownWindow_PreviewKeyDown;
            Deactivated += DropDownWindow_Deactivated;
            Closing += DropDownWindow_Closing;
            Closed += DropDownWindow_Closed;

            _contentPanel = new Grid() { Margin = new Thickness( 2 ) };
            _contentPanel.Children.Add( content );
            Content = _contentPanel;
        }

        #endregion C-tor

        #region Event handlers
        
        private void DropDownWindow_Deactivated( object sender, EventArgs e )
        {
            if ( !_closing )
            {
                DialogResult = true;
            }
        }

        private void DropDownWindow_PreviewKeyDown( object sender, KeyEventArgs e )
        {
            if ( e.Key == Key.Escape )
            {
                DialogResult = false;
            }
        }

        private void DropDownWindow_Closing( object sender, CancelEventArgs e )
        {
            _closing = true;
        }

        private void DropDownWindow_Closed( object sender, EventArgs e )
        {
            _contentPanel.Children.Clear();
        }
        
        #endregion Event handlers
    }

    /// <summary>
    ///     Combo-like control that contains custom editor and drop-down panel. Meant to be used as base class.
    /// </summary>
    [ContentProperty("Content")]
    public class DropDown : ElementFacade<DropDownContent>
    {
        #region Data
        
        private DropDownWindow _window;
        
        #endregion Data

        #region Properties
        
        public new static readonly DependencyProperty ContentProperty =
            DependencyProperty.Register( "Content", typeof( FrameworkElement ), typeof( DropDown ) );

        public static readonly DependencyProperty PanelProperty =
            DependencyProperty.Register( "Panel", typeof( FrameworkElement ), typeof( DropDown ) );

        public static readonly DependencyProperty TypeProperty =
            DependencyProperty.Register( "Type", typeof( RedDialogType ), typeof( DropDown ), 
                new FrameworkPropertyMetadata( RedDialogType.NoButtons, TypePropertyChanged ) );

        /// <summary>
        ///     Content of DropDown (next to drop-down button)
        /// </summary>
        public new FrameworkElement Content
        {
            get { return (FrameworkElement)GetValue( ContentProperty ); }
            set { SetValue( ContentProperty, value ); }
        }

        /// <summary>
        ///     Content of selector area 
        /// </summary>
        public FrameworkElement Panel
        {
            get { return (FrameworkElement)GetValue( PanelProperty ); }
            set { SetValue( PanelProperty, value ); }
        }

        /// <summary>
        ///     Type of the drop-down dialog.
        /// </summary>
        public RedDialogType Type
        {
            get { return (RedDialogType)GetValue( TypeProperty ); }
            set { SetValue( TypeProperty, value ); }
        }

        #endregion Properties

        #region Events

        /// <summary>
        ///     Arguments for PanelClosed event handler
        /// </summary>
        public class PanelClosedEventArgs : EventArgs
        {
            public bool Result { get; private set; }

            public PanelClosedEventArgs( bool result )
            {
                Result = result;
            }
        }

        /// <summary>
        ///     Handler delegate for PanelClosed event
        /// </summary>
        public delegate void PanelClosedEventHandler( object sender, PanelClosedEventArgs e );

        /// <summary>
        ///     Called after drop-down panel is closed
        /// </summary>
        public event PanelClosedEventHandler PanelClosed;
        
        #endregion Events

        #region C-tor
        
        public DropDown()
        {
            Element.Owner = this;
        }
        
        #endregion C-tor

        #region Public methods
        
        /// <summary>
        ///     Shows drop window non-modally
        /// </summary>
        public async Task<bool> DropAsync()
        {
            if ( _window != null ) // already open
                return false;

            _window = new DropDownWindow( this, Panel, null, Type );
            _window.Loaded += ( s, e ) => { AdjustWindowPosition(); };
            bool result = await _window.ShowAsync();
            _window = null;
            if ( PanelClosed != null )
            {
                PanelClosed( this, new PanelClosedEventArgs( result ) );
            }
            return result;
        }
        
        #endregion Public methods

        #region Private methods
        
        protected void CloseDropDown( bool result )
        {
            if ( _window != null )
            {
                _window.DialogResult = result;
            }
        }

        private void AdjustWindowPosition()
        {
            var globalPos = PointToScreen( new Point( 0.0, 0.0 ) );
            Rect monitorRect = MonitorUtils.GetMonitorWorkingSizeFromWindow( _window );

            if ( globalPos.Y + ActualHeight + _window.ActualHeight < monitorRect.Bottom )
                _window.Top = globalPos.Y + ActualHeight;
            else // drop up instead of down
                _window.Top = globalPos.Y - _window.ActualHeight;

            if ( globalPos.X >= monitorRect.Left )
            {
                if ( globalPos.X + _window.ActualWidth <= monitorRect.Right )
                    _window.Left = globalPos.X;
                else
                    _window.Left = monitorRect.Right - _window.ActualWidth;
            }
            else
                _window.Left = monitorRect.Left;
        }
        
        #endregion Private methods

        #region Event handlers
        
        private static void TypePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            var self = (DropDown)d;
            if ( self._window != null )
            {
                self._window.Type = (RedDialogType)e.NewValue;
            }
        }
        
        #endregion Event handlers
    }
}
